﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Xps.Packaging;
using System.IO;
using System.IO.Compression;
using Microsoft.Win32;
using Microsoft.Kinect;
using System.Reflection;
using Coding4Fun.Kinect.Wpf;
using System.Diagnostics;
using NAudio.Wave;
using NAudio.CoreAudioApi;
using NAudio.Wave.Compression;
using NAudioDemo.NetworkChatDemo;
using NAudio.Wave.SampleProviders;


namespace Powerpoint
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Vars Statistics
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        private Statistics stats = new Statistics();

        //Kinect sensor
        private KinectSensor sensor;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Vars Video
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Bitmap that will hold color information
        private WriteableBitmap colorBitmap;

        // Intermediate storage for the color data received from the camera
        private byte[] colorPixels;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Vars Skeleton
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Width of output drawing
        /// </summary>
        private const float RenderWidth = 640.0f;

        /// <summary>
        /// Height of our output drawing
        /// </summary>
        private const float RenderHeight = 480.0f;

        /// <summary>
        /// Thickness of drawn joint lines
        /// </summary>
        private const double JointThickness = 3;

        /// <summary>
        /// Thickness of body center ellipse
        /// </summary>
        private const double BodyCenterThickness = 10;

        /// <summary>
        /// Thickness of clip edge rectangles
        /// </summary>
        private const double ClipBoundsThickness = 10;

        /// <summary>
        /// Brush used to draw skeleton center point
        /// </summary>
        private readonly Brush centerPointBrush = Brushes.Blue;

        /// <summary>
        /// Brush used for drawing joints that are currently tracked
        /// </summary>
        private readonly Brush trackedJointBrush = new SolidColorBrush(Color.FromArgb(255, 68, 192, 68));

        /// <summary>
        /// Brush used for drawing joints that are currently inferred
        /// </summary>        
        private readonly Brush inferredJointBrush = Brushes.Yellow;

        /// <summary>
        /// Pen used for drawing bones that are currently tracked
        /// </summary>
        private readonly Pen trackedBonePen = new Pen(Brushes.Green, 6);

        /// <summary>
        /// Pen used for drawing bones that are currently inferred
        /// </summary>        
        private readonly Pen inferredBonePen = new Pen(Brushes.Gray, 1);

        /// <summary>
        /// Drawing group for skeleton rendering output
        /// </summary>
        private DrawingGroup drawingGroup;

        /// <summary>
        /// Drawing image that we will display
        /// </summary>
        private DrawingImage imageSource;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Vars Audio
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private WaveIn waveIn;
        private IWavePlayer waveOut;
        private BufferedWaveProvider waveProvider;
        private INetworkChatCodec codec;

        private DateTime lastStatCalc;
        BitmapImage bi = new BitmapImage();

        int inputDeviceNumber;
        MeteringSampleProvider postVolumeMeter;

        float currentVolume = 0;

        /// <summary>
        /// Number of milliseconds between each read of audio data from the stream.
        /// </summary>
        private const int AudioPollingInterval = 50;

        /// <summary>
        /// Number of samples captured from Kinect audio stream each millisecond.
        /// </summary>
        private const int SamplesPerMillisecond = 16;

        /// <summary>
        /// Number of bytes in each Kinect audio stream sample.
        /// </summary>
        private const int BytesPerSample = 2;

        /// <summary>
        /// Number of audio samples represented by each column of pixels in wave bitmap.
        /// </summary>
        private const int SamplesPerColumn = 40;

        /// <summary>
        /// Width of bitmap that stores audio stream energy data ready for visualization.
        /// </summary>
        private const int EnergyBitmapWidth = 780;

        /// <summary>
        /// Height of bitmap that stores audio stream energy data ready for visualization.
        /// </summary>
        private const int EnergyBitmapHeight = 195;

        /// <summary>
        /// Rectangle representing the entire energy bitmap area. Used when drawing background
        /// for energy visualization.
        /// </summary>
        private readonly Int32Rect fullEnergyRect = new Int32Rect(0, 0, EnergyBitmapWidth, EnergyBitmapHeight);

        /// <summary>
        /// Array of background-color pixels corresponding to an area equal to the size of whole energy bitmap.
        /// </summary>
        private readonly byte[] backgroundPixels = new byte[EnergyBitmapWidth * EnergyBitmapHeight];

        /// <summary>
        /// Buffer used to hold audio data read from audio stream.
        /// </summary>
        private readonly byte[] audioBuffer = new byte[AudioPollingInterval * SamplesPerMillisecond * BytesPerSample];

        /// <summary>
        /// Buffer used to store audio stream energy data as we read audio.
        /// 
        /// We store 25% more energy values than we strictly need for visualization to allow for a smoother
        /// stream animation effect, since rendering happens on a different schedule with respect to audio
        /// capture.
        /// </summary>
        private readonly double[] energy = new double[(uint)(EnergyBitmapWidth * 1.25)];



        //UEBERTRAGUNG VOM VIDEO

        int videoWaitCounter = 0;

        /// <summary>
        /// Stream of audio being captured by Kinect sensor.
        /// </summary>
        private Stream audioStream;


        bool firstMotionChapture = true;
        private float[,] skeletonXPositions = new float[3, 999];
        private float[,] skeletonYPositions = new float[3, 999];
        private float[,] skeletonZPositions = new float[3, 999];
        private float[] oldMotionSum = new float[999];

        private int[] waitCounter = new int[999];
        private long[] motionCounter = new long[999];


        //Start
        public MainWindow()
        {
            InitializeComponent();
            PopulateInputDevicesCombo();

            initCodec();

            //Get version
            labelVersion.Content = Assembly.GetExecutingAssembly().GetName().Version.ToString();
        }

        private void initCodec()
        {
            this.codec = new G722ChatCodec();
        }

        private void PopulateInputDevicesCombo()
        {
            for (int n = 0; n < WaveIn.DeviceCount; n++)
            {
                var capabilities = WaveIn.GetCapabilities(n);

                this.comboBoxAudioDevices.Items.Add(capabilities.ProductName);
            }
            if (comboBoxAudioDevices.Items.Count > 0)
            {
                comboBoxAudioDevices.SelectedIndex = 0;
            }
        }
        class CodecComboItem
        {
            public string Text { get; set; }
            public INetworkChatCodec Codec { get; set; }
            public override string ToString()
            {
                return Text;
            }
        }


        // Delegate
        private delegate void OneArgDelegate(String arg);


        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Methods Presentation
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////


        /// <summary>
        /// User opens presentation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_File_Click(object sender, RoutedEventArgs e)
        {
            //File dialog and filter
            Microsoft.Win32.OpenFileDialog fileDialog = new Microsoft.Win32.OpenFileDialog();
            fileDialog.DefaultExt = ".xps";
            fileDialog.Filter = "Xps Documents (*.xps)|*.xps";

            //Show
            Nullable<bool> result = fileDialog.ShowDialog();

            //User selected file
            if (result == true)
            {
                if (fileDialog.FileName.Length > 0)
                {
                    //get filename
                    string newXPSDocumentName = String.Concat(System.IO.Path.GetDirectoryName(fileDialog.FileName), "\\",
                                   System.IO.Path.GetFileNameWithoutExtension(fileDialog.FileName), ".xps");

                    //create xps document and load
                    XpsDocument xps = new XpsDocument(newXPSDocumentName, System.IO.FileAccess.Read);
                    documentViewer1.Document = xps.GetFixedDocumentSequence();

                    //Adjust zoom
                    documentViewer1.Zoom = 45;

                    //show control buttons

                    button_Next.Visibility = System.Windows.Visibility.Visible;
                    button_previous.Visibility = System.Windows.Visibility.Visible;

                }
            }

            //ToDo: Send to Server
        }


        private void button_previous_Click(object sender, RoutedEventArgs e)
        {
            if (comboBox1.SelectedIndex.ToString().Equals("0"))
            {
                int actual_page = documentViewer1.MasterPageNumber - 1;
                clientConnection.chandler.addStringData("$previousSlide$" + actual_page + "$@EOF");
                previousSlide();
            }
            else
            {
                previousSlide();
            }
        }

        public void previousSlideEvent(int page)
        {
            button_previous_Click(new Object(), new RoutedEventArgs());
            previousSlide(page);
        }

        public void updateImg1(byte[] buffer)
        {

            byte[] bufferSmaller = new byte[1228800];
            for (int i = 0; i < bufferSmaller.Length; i++)
            {
                bufferSmaller[i] = buffer[i];
            }



            WriteableBitmap colorBmp = new WriteableBitmap(640, 480, 96.0, 96.0, PixelFormats.Bgr32, null);
            colorBmp.WritePixels(
                new Int32Rect(0, 0, colorBmp.PixelWidth, colorBmp.PixelHeight),
                bufferSmaller,
                colorBmp.PixelWidth * sizeof(int),
                0);
            this.imageUser1.Source = colorBmp;

        }

        private void button_Next_Click(object sender, RoutedEventArgs e)
        {
            if (comboBox1.SelectedIndex.ToString().Equals("0")) // Server Mode (0) or Client Mode (1)?
            {
                int actual_page = documentViewer1.MasterPageNumber + 1;
                clientConnection.chandler.addStringData("$nextSlide$" + actual_page + "$@EOF");
                nextSilde();

            }
            else
            {
                nextSilde();
            }
        }

        public void nextSlideEvent(int page)
        {
            button_Next_Click(new Object(), new RoutedEventArgs());
            nextSlide(page);
        }

        private void nextSilde()
        {
            documentViewer1.NextPage();
        }

        private void nextSlide(int page)
        {
            documentViewer1.GoToPage(page);
        }

        private void previousSlide()
        {
            documentViewer1.PreviousPage();
        }

        private void previousSlide(int page)
        {
            documentViewer1.GoToPage(page);
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Methods Video and Skeleton
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        private void buttonStartVideo_Click(object sender, RoutedEventArgs e)
        {
            //Searching for Kinect
            foreach (var potentialSensor in KinectSensor.KinectSensors)
            {
                if (potentialSensor.Status == KinectStatus.Connected)
                {
                    this.sensor = potentialSensor;
                    break;
                }
            }

            if (null != this.sensor)
            {
                //Kinect found

                //Video
                // Turn on the color stream to receive color frames
                this.sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
                // Allocate space to put the pixels we'll receive
                this.colorPixels = new byte[this.sensor.ColorStream.FramePixelDataLength];
                // This is the bitmap we'll display on-screen
                this.colorBitmap = new WriteableBitmap(this.sensor.ColorStream.FrameWidth, this.sensor.ColorStream.FrameHeight, 96.0, 96.0, PixelFormats.Bgr32, null);
                // Set the image we display to point to the bitmap where we'll put the image data
                this.imageMe.Source = this.colorBitmap;

                //Skeleton
                // Add an event handler to be called whenever there is new color frame data
                this.sensor.ColorFrameReady += this.SensorColorFrameReady;
                //Set Trackingmode to seated
                this.sensor.SkeletonStream.TrackingMode = SkeletonTrackingMode.Seated;
                // Turn on the skeleton stream to receive skeleton frames
                this.sensor.SkeletonStream.Enable();
                // Add an event handler to be called whenever there is new color frame data
                this.sensor.SkeletonFrameReady += this.SensorSkeletonFrameReady;
                // Create the drawing group we'll use for drawing
                this.drawingGroup = new DrawingGroup();
                // Create an image source that we can use in our image control
                this.imageSource = new DrawingImage(this.drawingGroup);
                // Display the drawing using our image control
                imageUser3.Source = this.imageSource;


                //Audio
                inputDeviceNumber = comboBoxAudioDevices.SelectedIndex;


                
                    waveIn = new WaveIn();
                    waveIn.BufferMilliseconds = 50;
                    waveIn.DeviceNumber = inputDeviceNumber;
                    waveIn.WaveFormat = codec.RecordFormat;
                    waveIn.DataAvailable += waveIn_DataAvailable;
                    waveIn.StartRecording();
                

                waveOut = new WaveOut();
                waveProvider = new BufferedWaveProvider(codec.RecordFormat);

                var waveChannel = new SampleChannel(waveProvider);

                waveChannel.PreVolumeMeter += OnPreVolumeMeter;

                postVolumeMeter = new MeteringSampleProvider(waveChannel);
                postVolumeMeter.StreamVolume += OnPostVolumeMeter;

                //waveOut.Init(waveProvider);
                waveOut.Init(new SampleToWaveProvider(postVolumeMeter));
                waveOut.Play();






                this.comboBoxAudioDevices.AllowDrop = false;


                // Start the sensor
                try
                {
                    this.sensor.Start();

                    // Start streaming audio!
                    this.audioStream = this.sensor.AudioSource.Start();


                    this.Dispatcher.Invoke((Action)(() =>
                    {
                        System.Threading.Thread.Sleep(1000);
                        this.lblVolume.Content = string.Format("{0:0.0%}", this.currentVolume);
                    }));


                    updateEventBox("Video gestartet");
                    updateEventBox("Skeleton gestartet");
                    updateEventBox("Audio gestartet");

                }
                catch (IOException)
                {
                    this.sensor = null;
                }
            }

            if (null == this.sensor)
            {
                labelStatus.Content = "Leider konnte keine Kinect gefunden werden.";
            }
        }

        /// <summary>
        /// Ends kinects video
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonStopVideo_Click(object sender, RoutedEventArgs e)
        {
            //Stop Kinect
            if (null != this.sensor)
            {
                this.sensor.AudioSource.Stop();
                this.sensor.Stop();
            }

            if (null != waveIn)
            {
                waveIn.DataAvailable -= waveIn_DataAvailable;
                waveIn.StopRecording();
                waveOut.Stop();

                waveIn.Dispose();
                waveOut.Dispose();

                this.codec.Dispose();
            }

            //Reset Images
            imageMe.Source = new BitmapImage(new Uri("images/userDefault.gif", UriKind.Relative));
            imageUser3.Source = new BitmapImage(new Uri("images/userDefault.gif", UriKind.Relative));

            updateEventBox("Video beendet");
            updateEventBox("Skeleton beendet");
            updateEventBox("Audio bendet");


        }

        void waveIn_DataAvailable(object sender, WaveInEventArgs e)
        {
            byte[] encoded = codec.Encode(e.Buffer, 0, e.BytesRecorded);
            byte[] decoded = codec.Decode(encoded, 0, encoded.Length);
            waveProvider.AddSamples(decoded, 0, decoded.Length);




            //Marius hier musst du senden.
            //udpSender.Send(encoded, encoded.Length);
        }

        /// <summary>
        /// Execute shutdown tasks
        /// </summary>
        /// <param name="sender">object sending the event</param>
        /// <param name="e">event arguments</param>
        private void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // Tell audio reading thread to stop and wait for it to finish.

            waveIn.DataAvailable -= waveIn_DataAvailable;
            waveIn.StopRecording();
            waveOut.Stop();

            waveIn.Dispose();
            waveOut.Dispose();

            this.codec.Dispose();



            if (null != this.sensor)
            {
                this.sensor.AudioSource.Stop();
                this.sensor.Stop();
            }
        }


        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Methods Video
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Event handler for Kinect sensor's ColorFrameReady event
        /// </summary>
        /// <param name="sender">object sending the event</param>
        /// <param name="e">event arguments</param>
        /// 



        //


        private void SensorColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
        {



            using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
            {
                if (colorFrame != null)
                {
                    // Copy the pixel data from the image to a temporary array
                    colorFrame.CopyPixelDataTo(this.colorPixels);

                    //ToDo
                    if (clientConnection != null)
                    {
                        if (clientConnection.getConnectionStatus())
                        {

                            // Der Mumpitz funktioniert nicht richtig, das Speicherhandling klappt nicht.
                            // Delegte Ressourcen werden nach Gebrauch nicht freigegeben.
                            // Speicher laeuft mit byte[] voll.
                            //using (MemoryStream ms = new MemoryStream())
                            //{
                            //    byte[] toSend = null;
                            //    ms.Write(this.colorPixels, 0, this.colorPixels.Length);

                            //    //using (StreamWriter writer = new StreamWriter(ms))
                            //    //{
                            //    //   writer.Write("@EOF");
                            //    //   writer.Dispose();
                            //    //}
                            //    toSend = ms.ToArray();
                            //    ms.Dispose();
                            //    clientConnection.chandler.addByteData(toSend);
                            //}

                            //byte[] tmp = Compress(this.colorPixels);
                            if (videoWaitCounter < 10)
                            {
                                videoWaitCounter++;
                            }
                            else
                            {
                                videoWaitCounter = 0;
                                byte[] toSend;
                                using (MemoryStream ms = new MemoryStream())
                                {


                                    ms.Write(this.colorPixels, 0, this.colorPixels.Length);

                                    toSend = ms.ToArray();

                                }

                                // Machen wa et so...
                                clientConnection.chandler.addByteData(toSend);
                                clientConnection.chandler.addStringData("@EOF");

                                //////////////////////////////////////////////

                                //clientConnection.chandler.addByteStreamData(this.colorPixels);
                            }
                        }
                    }
                    // Write the pixel data into our bitmap
                    this.colorBitmap.WritePixels(
                        new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight),
                        this.colorPixels,
                        this.colorBitmap.PixelWidth * sizeof(int),
                        0);



                }
            }
        }

        //public static byte[] Compress(byte[] raw)
        //{
        //    using (MemoryStream memory = new MemoryStream())
        //    {
        //        using (GZipStream gzip = new GZipStream(memory, CompressionMode.Compress, true))
        //        {
        //            gzip.Write(raw, 0, raw.Length);
        //        }
        //        return memory.ToArray();
        //    }
        //}

        public static byte[] Combine(byte[] first, byte[] second)
        {
            byte[] ret = new byte[first.Length + second.Length];
            Buffer.BlockCopy(first, 0, ret, 0, first.Length);
            Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
            return ret;
        }

        static string GetString(byte[] bytes)
        {
            char[] chars = new char[bytes.Length / sizeof(char)];
            System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
            return new string(chars);
        }


        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Methods Audio
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////


        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Methods Misc
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Reset Events
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonClearEvents_Click(object sender, RoutedEventArgs e)
        {
            textBoxEvents.Text = "";
        }


        /// <summary>
        /// Adds new Event
        /// </summary>
        /// <param name="newEvent"></param>
        public void updateEventBox(string newEventItem)
        {
            if (textBoxEvents.Text.Equals(""))
            {
                textBoxEvents.Text = newEventItem;
            }
            else
            {
                textBoxEvents.Text += "\r\n" + newEventItem;
            }
        }

        public void updateDataBox(string str_nbDataPkg)
        {
            nbDataPkg.Text = str_nbDataPkg;
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Methods Skeleton
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Draws indicators to show which edges are clipping skeleton data
        /// </summary>
        /// <param name="skeleton">skeleton to draw clipping information for</param>
        /// <param name="drawingContext">drawing context to draw to</param>
        private static void RenderClippedEdges(Skeleton skeleton, DrawingContext drawingContext)
        {
            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Bottom))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, RenderHeight - ClipBoundsThickness, RenderWidth, ClipBoundsThickness));
            }

            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Top))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, 0, RenderWidth, ClipBoundsThickness));
            }

            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Left))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, 0, ClipBoundsThickness, RenderHeight));
            }

            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Right))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(RenderWidth - ClipBoundsThickness, 0, ClipBoundsThickness, RenderHeight));
            }
        }


        /// <summary>
        /// Event handler for Kinect sensor's SkeletonFrameReady event
        /// </summary>
        /// <param name="sender">object sending the event</param>
        /// <param name="e">event arguments</param>
        private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            Skeleton[] skeletons = new Skeleton[0];

            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            {
                if (skeletonFrame != null)
                {
                    skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];

                    skeletonFrame.CopySkeletonDataTo(skeletons);

                }
            }

            using (DrawingContext dc = this.drawingGroup.Open())
            {
                // Draw a transparent background to set the render size
                dc.DrawRectangle(Brushes.Black, null, new Rect(0.0, 0.0, RenderWidth, RenderHeight));

                if (skeletons.Length != 0)
                {
                    foreach (Skeleton skel in skeletons)
                    {
                        RenderClippedEdges(skel, dc);

                        if (waitCounter[skel.TrackingId] == 40)
                        {
                            waitCounter[skel.TrackingId] = 0;

                            if (skel != null)
                            {
                                checkNotification(skel);
                                calcMotionIndex(skel);
                            }

                        }
                        else
                        {
                            waitCounter[skel.TrackingId]++;
                        }

                        if (skel.TrackingState == SkeletonTrackingState.Tracked)
                        {
                            this.DrawBonesAndJoints(skel, dc);
                        }
                        else if (skel.TrackingState == SkeletonTrackingState.PositionOnly)
                        {
                            dc.DrawEllipse(
                            this.centerPointBrush,
                            null,
                            this.SkeletonPointToScreen(skel.Position),
                            BodyCenterThickness,
                            BodyCenterThickness);
                        }
                    }
                }

                // prevent drawing outside of our render area
                this.drawingGroup.ClipGeometry = new RectangleGeometry(new Rect(0.0, 0.0, RenderWidth, RenderHeight));
            }
        }

        /// <summary>
        /// Draws a skeleton's bones and joints
        /// </summary>
        /// <param name="skeleton">skeleton to draw</param>
        /// <param name="drawingContext">drawing context to draw to</param>
        private void DrawBonesAndJoints(Skeleton skeleton, DrawingContext drawingContext)
        {
            // Render Torso
            this.DrawBone(skeleton, drawingContext, JointType.Head, JointType.ShoulderCenter);
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderLeft);
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderRight);
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.Spine);
            this.DrawBone(skeleton, drawingContext, JointType.Spine, JointType.HipCenter);
            this.DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.HipLeft);
            this.DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.HipRight);

            // Left Arm
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderLeft, JointType.ElbowLeft);
            this.DrawBone(skeleton, drawingContext, JointType.ElbowLeft, JointType.WristLeft);
            this.DrawBone(skeleton, drawingContext, JointType.WristLeft, JointType.HandLeft);

            // Right Arm
            this.DrawBone(skeleton, drawingContext, JointType.ShoulderRight, JointType.ElbowRight);
            this.DrawBone(skeleton, drawingContext, JointType.ElbowRight, JointType.WristRight);
            this.DrawBone(skeleton, drawingContext, JointType.WristRight, JointType.HandRight);

            // Left Leg
            this.DrawBone(skeleton, drawingContext, JointType.HipLeft, JointType.KneeLeft);
            this.DrawBone(skeleton, drawingContext, JointType.KneeLeft, JointType.AnkleLeft);
            this.DrawBone(skeleton, drawingContext, JointType.AnkleLeft, JointType.FootLeft);

            // Right Leg
            this.DrawBone(skeleton, drawingContext, JointType.HipRight, JointType.KneeRight);
            this.DrawBone(skeleton, drawingContext, JointType.KneeRight, JointType.AnkleRight);
            this.DrawBone(skeleton, drawingContext, JointType.AnkleRight, JointType.FootRight);

            // Render Joints
            foreach (Joint joint in skeleton.Joints)
            {
                Brush drawBrush = null;

                if (joint.TrackingState == JointTrackingState.Tracked)
                {
                    drawBrush = this.trackedJointBrush;
                }
                else if (joint.TrackingState == JointTrackingState.Inferred)
                {
                    drawBrush = this.inferredJointBrush;
                }

                if (drawBrush != null)
                {
                    drawingContext.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(joint.Position), JointThickness, JointThickness);
                }
            }
        }

        /// <summary>
        /// Maps a SkeletonPoint to lie within our render space and converts to Point
        /// </summary>
        /// <param name="skelpoint">point to map</param>
        /// <returns>mapped point</returns>
        private Point SkeletonPointToScreen(SkeletonPoint skelpoint)
        {
            // Convert point to depth space.  
            // We are not using depth directly, but we do want the points in our 640x480 output resolution.
            DepthImagePoint depthPoint = this.sensor.MapSkeletonPointToDepth(
                                                                             skelpoint,
                                                                             DepthImageFormat.Resolution640x480Fps30);
            return new Point(depthPoint.X, depthPoint.Y);
        }


        /// <summary>
        /// Draws a bone line between two joints
        /// </summary>
        /// <param name="skeleton">skeleton to draw bones from</param>
        /// <param name="drawingContext">drawing context to draw to</param>
        /// <param name="jointType0">joint to start drawing from</param>
        /// <param name="jointType1">joint to end drawing at</param>
        private void DrawBone(Skeleton skeleton, DrawingContext drawingContext, JointType jointType0, JointType jointType1)
        {
            Joint joint0 = skeleton.Joints[jointType0];
            Joint joint1 = skeleton.Joints[jointType1];

            // If we can't find either of these joints, exit
            if (joint0.TrackingState == JointTrackingState.NotTracked ||
                joint1.TrackingState == JointTrackingState.NotTracked)
            {
                return;
            }

            // Don't draw if both points are inferred
            if (joint0.TrackingState == JointTrackingState.Inferred &&
                joint1.TrackingState == JointTrackingState.Inferred)
            {
                return;
            }

            // We assume all drawn bones are inferred unless BOTH joints are tracked
            Pen drawPen = this.inferredBonePen;
            if (joint0.TrackingState == JointTrackingState.Tracked && joint1.TrackingState == JointTrackingState.Tracked)
            {
                drawPen = this.trackedBonePen;
            }

            drawingContext.DrawLine(drawPen, this.SkeletonPointToScreen(joint0.Position), this.SkeletonPointToScreen(joint1.Position));
        }

        public void calcMotionIndex(Skeleton skel)
        {
            if (skel.TrackingState == SkeletonTrackingState.Tracked)
            {


                Joint head = skel.Joints[JointType.Head]; // in arrays allways "0"
                Joint handRight = skel.Joints[JointType.HandRight];// in arrays allways "1"
                Joint handLeft = skel.Joints[JointType.HandLeft];// in arrays allways "2"

                float[] diffX = new float[3];
                float[] diffY = new float[3];
                float[] diffZ = new float[3];
                float[] diffSum = new float[3];

                //calc head
                if (!firstMotionChapture)
                {
                    diffX[0] = (float)Math.Pow(Convert.ToDouble(skeletonXPositions[0, skel.TrackingId] - handRight.Position.X), 2);
                    diffY[0] = (float)Math.Pow(Convert.ToDouble(skeletonYPositions[0, skel.TrackingId] - head.Position.Y), 2);
                    diffZ[0] = (float)Math.Pow(Convert.ToDouble(skeletonZPositions[0, skel.TrackingId] - head.Position.Z), 2);
                    diffSum[0] = diffX[0] + diffY[0] + diffZ[0];
                }
                skeletonXPositions[0, skel.TrackingId] = head.Position.X;
                skeletonYPositions[0, skel.TrackingId] = head.Position.Y;
                skeletonZPositions[0, skel.TrackingId] = head.Position.Z;



                //calc righthan
                if (!firstMotionChapture)
                {
                    diffX[1] = (float)Math.Pow(Convert.ToDouble(skeletonXPositions[1, skel.TrackingId] - handRight.Position.X), 2);
                    diffY[1] = (float)Math.Pow(Convert.ToDouble(skeletonYPositions[1, skel.TrackingId] - handRight.Position.Y), 2);
                    diffZ[1] = (float)Math.Pow(Convert.ToDouble(skeletonZPositions[1, skel.TrackingId] - handRight.Position.Z), 2);
                    diffSum[1] = diffX[1] + diffY[1] + diffZ[1];
                }
                skeletonXPositions[1, skel.TrackingId] = handRight.Position.X;
                skeletonYPositions[1, skel.TrackingId] = handRight.Position.Y;
                skeletonZPositions[1, skel.TrackingId] = handRight.Position.Z;



                //calc lefthand
                if (!firstMotionChapture)
                {
                    diffX[2] = (float)Math.Pow(Convert.ToDouble(skeletonXPositions[2, skel.TrackingId] - handLeft.Position.X), 2);
                    diffY[2] = (float)Math.Pow(Convert.ToDouble(skeletonYPositions[2, skel.TrackingId] - handLeft.Position.Y), 2);
                    diffZ[2] = (float)Math.Pow(Convert.ToDouble(skeletonZPositions[2, skel.TrackingId] - handLeft.Position.Z), 2);
                    diffSum[2] = diffX[2] + diffY[2] + diffZ[2];
                }

                skeletonXPositions[2, skel.TrackingId] = handLeft.Position.X;
                skeletonYPositions[2, skel.TrackingId] = handLeft.Position.Y;
                skeletonZPositions[2, skel.TrackingId] = handLeft.Position.Z;


                //calc complet
                if (!firstMotionChapture)
                {
                    float diffSumCollective = diffSum[0] + diffSum[1] + diffSum[2];


                    //refresh stats
                    stats.addMotion(diffSumCollective);

                    motionCounter[skel.TrackingId]++;

                    oldMotionSum[skel.TrackingId] = (diffSumCollective + (oldMotionSum[skel.TrackingId] * (motionCounter[skel.TrackingId] - 1))) / motionCounter[skel.TrackingId];
                    tbMotionIndex.Text = oldMotionSum[skel.TrackingId].ToString();
                }
                firstMotionChapture = false;
            }

        }


        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Methods Notification
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        //Current Notification?
        Boolean currentNotification = false;

        /// <summary>
        /// Checks for new Notification
        /// </summary>
        /// <param name="skel"></param>
        public void checkNotification(Skeleton skel)
        {
            Joint head = skel.Joints[JointType.Head]; // in arrays allways "0"
            Joint handRight = skel.Joints[JointType.HandRight];// in arrays allways "1"
            Joint handLeft = skel.Joints[JointType.HandLeft];// in arrays allways "2"

            if (head.Position.Y != 0 && handRight.Position.Y != 0 && handLeft.Position.Y != 0 && (head.Position.Y < handRight.Position.Y || head.Position.Y < handLeft.Position.Y))
            {
                //Notification 
                tbNotification.Text = "ja";
                if (!currentNotification)
                {
                    stats.addNotification();
                    if (clientConnection != null)
                    {
                        clientConnection.chandler.addStringData("$notificationTrue$" + "$@EOF");
                    }
                }
                rbNotificationUser3.IsChecked = true;
                currentNotification = true;
            }
            else
            {
                if (head.Position.Y != 0 && handRight.Position.Y != 0 && handLeft.Position.Y != 0)
                {
                    //Data, but no notification
                    tbNotification.Text = "nein";

                    if (currentNotification && (clientConnection != null))
                    {
                        clientConnection.chandler.addStringData("$notificationFalse$" + "$@EOF");
                        //ToDo Sende an Server, dass ich mich nicht mehr melde
                    }

                    currentNotification = false;
                    rbNotificationUser3.IsChecked = false;
                }
                //...else -> no data
            }

        }

        void OnPreVolumeMeter(object sender, NAudio.Wave.SampleProviders.StreamVolumeEventArgs e)
        {
            // we know it is stereo
            //Debug.Write("hallo    " +  e.MaxSampleValues[0]);
            //lblVolume.Content = e.MaxSampleValues[0];
        }

        void OnPostVolumeMeter(object sender, NAudio.Wave.SampleProviders.StreamVolumeEventArgs e)
        {
            //Debug.Write("hallo2    " + e.MaxSampleValues[0]);
            // we know it is stereo
            this.currentVolume = e.MaxSampleValues[0];
            Debug.WriteLine("Lautstärke");
            Debug.WriteLine(this.currentVolume);
            Debug.WriteLine(lblVolume.Content);
            //lblVolume.Content = e.MaxSampleValues[0];
            lblVolume.Content = string.Format("{0:0.0%}", this.currentVolume);
            if (lastStatCalc.AddSeconds(1) < DateTime.Now)
            {
                stats.addVolume(this.currentVolume);
                lastStatCalc = DateTime.Now;
            }
        }

        /// <summary>
        /// Event args for aggregated stream volume
        /// </summary>
        public class StreamVolumeEventArgs : EventArgs
        {
            /// <summary>
            /// Max sample values array (one for each channel)
            /// </summary>
            public float[] MaxSampleValues { get; set; }
        }

        /// <summary>
        /// New Notification received
        /// </summary>
        /// <param name="id">UserID</param>
        /// <param name="notification">Notification or not?</param>
        public void incomingNotification(int id, Boolean notification)
        {
            if (id == 1)
            {
                if (notification)
                {
                    iconUser1.Source = new BitmapImage(new Uri("images/user_comment.png", UriKind.Relative));
                }
                else
                {
                    iconUser1.Source = new BitmapImage(new Uri("images/user.png", UriKind.Relative));
                }
            }
            if (id == 2)
            {
                if (notification)
                {
                    iconUser2.Source = new BitmapImage(new Uri("images/user_comment.png", UriKind.Relative));
                }
                else
                {
                    iconUser2.Source = new BitmapImage(new Uri("images/user.png", UriKind.Relative));
                }
            }
            if (id == 3)
            {
                if (notification)
                {
                    iconUser3.Source = new BitmapImage(new Uri("images/user_comment.png", UriKind.Relative));
                }
                else
                {
                    iconUser3.Source = new BitmapImage(new Uri("images/user.png", UriKind.Relative));
                }
            }
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Methods Network
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public Thread clientThread;
        public Client clientConnection;

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            String ipAddress = textBox1.Text;
            Int32 port = int.Parse(textBox2.Text);

            clientConnection = new Client();
            clientConnection.setupConnection(ipAddress, port, this);

            clientThread = new Thread(clientConnection.StartClient);
            clientThread.Start();

            // Wait for connection completed
            Thread.Sleep(2000);
            if (clientConnection.getConnectionStatus())
            {
                labelStatus.Content = "Verbunden mit Server";
                labelStatus.Foreground = Brushes.Green;
            }
            else
            {
                labelStatus.Foreground = Brushes.Red;
                labelStatus.Content = "Es konnte keine Verbindung erstellt werden!";
            }
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            clientConnection.closeConnection();
            labelStatus.Foreground = Brushes.Black;
            labelStatus.Content = "Verbindung beendet";
        }

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            Random rand = new Random();
            double temp = rand.NextDouble() * 25;
            sensor.ElevationAngle = (int)temp;
            //clientConnection.chandler.addStringData("Muuuuuuuuuuuuuuuuuh<EOF>");
        }

        private void comboBoxCodecs_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }

        private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (comboBox1.SelectedIndex == 1)
            {
                button_File.Visibility = Visibility.Hidden;
                button_previous.Visibility = Visibility.Hidden;
                button_Next.Visibility = Visibility.Hidden;
            }
            else
            {
                button_File.Visibility = Visibility.Visible;
                button_previous.Visibility = Visibility.Visible;
                button_Next.Visibility = Visibility.Visible;
            }
        }

        private void btStats_Click(object sender, RoutedEventArgs e)
        {
            Statistics_Window statsWin = new Statistics_Window(stats);
            statsWin.Show();
        }

        private void image2_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            try
            {
               if (File.Exists("toll.mp3"))
                   System.Diagnostics.Process.Start("toll.mp3");
            }
            catch
            { }

        }

        private void imageUser2_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            try
            {
                if (true) { bi.BeginInit(); bi.UriSource = new Uri("images/toll.gif", UriKind.Relative); bi.EndInit(); imageUser2.Stretch = Stretch.Fill; imageUser2.Source = bi; }
            }
            catch { }
        }


        public void   setNotificationUser1True()
        {            
            rbNotificationUser1.IsChecked = true;
        }
        public void setNotificationUser1False()
        {
            rbNotificationUser1.IsChecked = false;
        }
    }

}
